<?php
/*
 * Copyright (c) 2013 Eugene Kuzmenko
 *
 * Permission  is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in  the Software without restriction, including without limitation the rights
 * to  use,  copy,  modify,  merge, publish, distribute, sublicense, and/or sell
 * copies  of  the  Software,  and  to  permit  persons  to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The  above  copyright  notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE  SOFTWARE  IS  PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED,  INCLUDING  BUT  NOT  LIMITED  TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS  FOR  A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS  OR  COPYRIGHT  HOLDERS  BE  LIABLE  FOR  ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * For more information, see <http://www.aljey.com>.
 */

namespace Al\Style\Lib\Form\Element;

use Al\Core\Lib\Service,
    Al\Style\Lib\Form\Field,
    Al\Doctrine\Lib\Em;

/**
 * An html file field or a rich GUI component for uploading files.
 *
 * @property bool $rich True if this is a rich file upload component.
 * @property bool $multiple True if you want to allow the selection of multiple
 *                          files.
 * @property array $filters An array of filters (named file types that are valid
 *                          for this field). E.g. [{title: "Image files",
 *                          extensions: "jpg,jpeg,gif,png"}]
 * @property int $limit Maximum file size. @see \Al\Core\Lib\Config::human2byte
 * @property string $name An html form field name.
 * @author Eugene Kuzmenko <mail@aljey.com>
 */
class File extends Element {

  /**
   * @var Text
   */
  protected $input;

  /**
   * @var array Files to move.
   */
  protected $move = array();

  /**
   * @var string Temporary directory path.
   */
  protected $tmp;

  /**
   * @param array $params Config array.
   * @param Field $field
   */
  public function __construct($params = array(), Field $field = null) {
    $this->configure(array(
      'filters' => array(),
      'limit' => false,
      'multiple' => true,
      'rich' => false,
      'name' => uniqid('name-'),
    ), $params);
    if ($this->limit) {
      $this->limit = Service::get('config')->human2byte($this->limit);
    }
    parent::__construct(array(), $field);
    if ($this->rich) {
      $this->tag = 'div';
      Service::set('http.params.plupload', true);
      $this->tmp = __DIR__ . "/../../../Resources/tmp/{$this->field->form->id}";
    } else {
      $params += array('attr' => array(), 'name' => $this->name);
      if ($this->multiple) {
        $params['attr']['multiple'] = 'multiple';
        $params['name'] .= '[]';
      }
      $this->input = new Text(array('inputType' => 'file') + $params,
        $this->field);
    }
    $class = Service::get('em')->entityShortClass($this->field->form->entity);
    $event = Service::get('event');
    $event->on("$class.postPersist", array($this, 'postPersist'));
    $event->on("$class.postUpdate", array($this, 'postPersist'));
    $event->on("$class.postRemove", array($this, 'postRemove'));
  }

  /**
   * Returns a string representation of a component.
   *
   * @return string
   */
  public function __toString() {
    if (!$this->rich) return "$this->input";
    $result = Service::get('template')->render(
      Service::get('config.plugins.Al\Style.form.plupload'), array(
        'id' => $this->id,
        'form_id' => $this->field->form->id,
        'action' => Service::get('request')->buildRoutePath('plupload_upload'),
        'filters' => $this->filters,
        'limit' => $this->limit,
        'multiple' => $this->multiple,
      ));
    $this->html = $result[0];
    return parent::__toString();
  }

  /** @inheritdoc */
  public function prePersist(Em $em, $entity) {
    if ($this->rich) {
      $move = &$this->move;
      $f = array_map(function ($file) use (&$move) {
        $move[] = array($file, 'upload/' . ($name = basename($file)));
        return $name;
      }, glob("$this->tmp/*"));
    } else {
      $f = array_map(function ($file) {
        return $file['uid'];
      }, Service::get("http.files.$this->name", array()));
    }
    if ($f) $em->entitySet($entity, $this->name, $this->multiple ? $f : $f[0]);
  }

  /**
   * Moves temporary files to a permanent storage location.
   */
  public function postPersist() {
    @mkdir('upload', 0777, true);
    if ($this->rich) {
      foreach ($this->move as $item) rename($item[0], $item[1]);
      @rmdir($this->tmp);
    } else {
      Service::get('http.files')->move('upload', $this->name);
    }
  }

  /**
   * Removes files associated with an entity when the entity is removed.
   *
   * @param Em $em
   * @param $entity
   */
  public function postRemove(Em $em, $entity) {
    $f = $em->entityGet($entity, $this->name);
    if (!$f) $f = array();
    if (!is_array($f)) $f = array($f);
    foreach ($f as $file) @unlink("upload/$file");
  }

}